package com.example.light.system.service;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.light.system.common.Result;
import com.example.light.system.dto.socketOutMessage;
import com.example.light.system.entity.announcement;
import com.example.light.system.entity.systemLog;
import com.example.light.system.mapper.systemLogMapper;
import com.example.light.system.mapper.AnnouncementMapper;
import com.example.light.system.service.impl.ISystemLogService;
import com.sun.management.OperatingSystemMXBean;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;


import javax.annotation.Resource;
import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class systemLogService extends ServiceImpl<systemLogMapper, systemLog> implements ISystemLogService {
    @Resource
    systemLogMapper systemLogMapper;
    @Resource
    webSocketService webSocketService;
    @Resource
    AnnouncementMapper announcementMapper;

    /**
     * 获取系统日志（cpu,磁盘，物理缓存，虚拟缓存）
     * @return
     * @throws InterruptedException
     */
    public Result<systemLog> getSystemLog() throws InterruptedException {
        // 获取systemLog实例
        systemLog systemLog = new systemLog();
        // 获取SystemInfo实例
        SystemInfo systemInfo = new SystemInfo();
        // 获取CentralProcessor实例
        CentralProcessor processor = systemInfo.getHardware().getProcessor();
        // 获取CPU核数
        int cpuCount = processor.getLogicalProcessorCount();
//        System.out.println("CPU核数："+cpuCount);

        // 获取系统范围的cpu负载技计数
        long[] prevTicks = processor.getSystemCpuLoadTicks();
        // 睡眠1s
        TimeUnit.SECONDS.sleep(1);
        long[] ticks = processor.getSystemCpuLoadTicks();

        // 具有良好优先级的用户级别
        long nice = ticks[CentralProcessor.TickType.NICE.ordinal()] - prevTicks[CentralProcessor.TickType.NICE.ordinal()];
        // 硬件服务
        long irq = ticks[CentralProcessor.TickType.IRQ.ordinal()] - prevTicks[CentralProcessor.TickType.IRQ.ordinal()];
        // 软服务使用
        long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.ordinal()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.ordinal()];
        // 管理程序使用
        long steal = ticks[CentralProcessor.TickType.STEAL.ordinal()] - prevTicks[CentralProcessor.TickType.STEAL.ordinal()];
        // 系统使用
        long cSys = ticks[CentralProcessor.TickType.SYSTEM.ordinal()] - prevTicks[CentralProcessor.TickType.SYSTEM.ordinal()];
        // 用户使用
        long user = ticks[CentralProcessor.TickType.USER.ordinal()] - prevTicks[CentralProcessor.TickType.USER.ordinal()];
        // 等待使用
        long iowait = ticks[CentralProcessor.TickType.IOWAIT.ordinal()] - prevTicks[CentralProcessor.TickType.IOWAIT.ordinal()];
        // 空闲使用
        long idle = ticks[CentralProcessor.TickType.IDLE.ordinal()] - prevTicks[CentralProcessor.TickType.IDLE.ordinal()];
        long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
        double sysRate = cSys * 1.0 / totalCpu;
        double userRate = user * 1.0 / totalCpu;
        double waitRate = cSys * 1.0 / totalCpu;
        double idleRate = cSys * 1.0 / totalCpu;
        double betweenRate = processor.getSystemCpuLoadBetweenTicks(prevTicks);
        double cpuLoad = Double.parseDouble(new DecimalFormat("#.00").format(processor.getSystemCpuLoad(3000)*100));
//        System.out.println("CPU使用率: "+ cpuLoad);
        systemLog.setSysCpu(cpuLoad);
//        System.out.println("cpu系统使用率:" + new DecimalFormat("#.##%").format(sysRate));
//        System.out.println("cpu用户使用率:" + new DecimalFormat("#.##%").format(userRate));
//        System.out.println("cpu当前等待率:" + new DecimalFormat("#.##%").format(waitRate));
//        System.out.println("cpu当前空闲率:" + new DecimalFormat("#.##%").format(idleRate));
//        // 获取cpu最近(时间间隔内)使用率
//        System.out.println("CPU load: "+ new DecimalFormat("#.##%").format(betweenRate) +"(counting ticks)");
        // 获取cpu使用率
//        System.out.println("CPU使用率: "+ new DecimalFormat("#.##%").format(cpuLoad));


        OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
        // 总内存，单位：字节
        long total = osmxb.getTotalPhysicalMemorySize();
        // 空闲内存，单位：字节
        long free = osmxb.getFreePhysicalMemorySize();
        // 可用内存，单位：字节
        long usable = osmxb.getFreePhysicalMemorySize();
        // 已使用内存，单位：字节
        long used = total - free;
        // 内存使用率
        double useRate = Double.parseDouble(new DecimalFormat("#.00").format(( used * 1.0/total)*100));
//        System.out.println("物理内存使用率：" + useRate);
        systemLog.setSysPhyMey(useRate);
//        System.out.println("总共内存：" + new DecimalFormat("#.##").format(total*1.0 / Math.pow(1024,3)) + "G");
//        System.out.println("空闲内存：" + new DecimalFormat("#.##").format(free*1.0 / Math.pow(1024,3)) + "G");
//        System.out.println("已用内存：" + new DecimalFormat("#.##").format(used*1.0 / Math.pow(1024,3)) + "G");
//        System.out.println("可用内存：" + new DecimalFormat("#.##").format(usable*1.0 / Math.pow(1024,3)) + "G");
//        System.out.println("物理内存使用率：" + new DecimalFormat("#.##%").format(useRate));

        // 虚拟内存占用率
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        // 椎内存使用情况
        MemoryUsage memoryUsage = memoryMXBean.getHeapMemoryUsage();
        // jvm初始总内存，单位：字节
        long initTotalMemorySize = memoryUsage.getInit();
        // jvm最大可用内存，单位：字节
        long freeV = osmxb.getFreePhysicalMemorySize();
        // jvm已使用的内存，单位：字节
        long usableV = osmxb.getFreePhysicalMemorySize();
        double VDiskUseRate = Double.parseDouble(new DecimalFormat("#.00").format((usableV/freeV)*100));
//        System.out.println("虚拟内存使用率：" + VDiskUseRate);
        systemLog.setSysVirMey(VDiskUseRate);
//        System.out.println("jvm初始总内存：" + new DecimalFormat("#.##").format(initTotalMemorySize*1.0 / Math.pow(1024,3)) + "G");
//        System.out.println("jvm最大可用内存：" + new DecimalFormat("#.##").format(freeV*1.0 / Math.pow(1024,3)) + "G");
//        System.out.println("jvm已使用的内存：" + new DecimalFormat("#.##").format(usableV*1.0 / Math.pow(1024,3)) + "G");
//        System.out.println("虚拟内存使用率：" + new DecimalFormat("#.##%").format(VDiskUseRate));

        // 统计总内存
        long totalDisk = 0;
        // 统计总空闲
        long freeDisk = 0;
        // 统计总可用
        long usableDisk = 0;
        // 统计总已用
        long usedDisk = 0;
        // 磁盘总使用
        double usedRate = 0.0;
        // 内存进制大小
        int RADIX = 1024;

        File[] disks = File.listRoots();
        Integer diskNumber = 0;
        double AllDiskUsedRate = 0.00;
        for (File file : disks){
            // 统计总量
            totalDisk += file.getTotalSpace();
            freeDisk += file.getFreeSpace();
            usableDisk += file.getUsableSpace();
            usedDisk += file.getTotalSpace() - file.getFreeSpace();

            String diskPath = file.getPath();
            long diskTotal = file.getTotalSpace();
            long diskFree = file.getFreeSpace();
            long diskUsable = file.getUsableSpace();
            long diskUsed = diskTotal - diskFree;
            double diskUsedRate = diskUsed * 1.0 / diskTotal;
            AllDiskUsedRate = AllDiskUsedRate + diskUsedRate;
            diskNumber = diskNumber + 1;
//            System.out.println("磁盘路径：" + diskPath);
//            System.out.println("总共空间："+ new DecimalFormat("#.##").format(diskTotal*1.0 / Math.pow(RADIX,3)) + "G");
//            System.out.println("空闲空间："+ new DecimalFormat("#.##").format(diskFree*1.0 / Math.pow(RADIX,3)) + "G");
//            System.out.println("可用空间："+ new DecimalFormat("#.##").format(diskUsable*1.0 / Math.pow(RADIX,3)) + "G");
//            System.out.println("已用空间："+ new DecimalFormat("#.##").format(diskUsed*1.0 / Math.pow(RADIX,3)) + "G");
//            System.out.println("空间使用率：" + new DecimalFormat("#.##%").format(diskUsedRate));
        }
        double AvgDiskUsedRate = Double.parseDouble(new DecimalFormat("#.00").format((AllDiskUsedRate/diskNumber)*100));
//        System.out.println("磁盘使用率：" + AvgDiskUsedRate);
        systemLog.setSysDisk(AvgDiskUsedRate);
//        System.out.println("磁盘使用率：" + new DecimalFormat("#.##%").format(AvgDiskUsedRate));
        // 新增系统日志
        int insert = systemLogMapper.insert(systemLog);
        if (insert == 1){
            return Result.success(systemLog);
        }else {
            return Result.fail("系统日志异常");
        }
    }

    /**
     * 新增系统公告信息
     *type:0-异常设备接入，1-设备上线，2-设备离线
     * @return
     */
    public Result<String> addAnnouncement(String deviceName,Integer type){
        // 实例化系统公告对象
        announcement announcement = new announcement();
        announcement.setAnnouncementType("【系统通知】");
        if (type == 0){
            announcement.setAnnouncementMessage("有异常设备接入，设备名称为："+deviceName+"，请及时确认设备来源，并添加设备");
        } else if (type == 1) {
            announcement.setAnnouncementMessage(deviceName+"设备已上线");
        } else if (type == 2) {
            announcement.setAnnouncementMessage(deviceName+"设备已离线");
        }
        LocalDateTime now = LocalDateTime.now();
        announcement.setAnnouncementTime(now);
        announcement.setAnnouncementIsRead(0);
        announcement.setAnnouncementIsDelete(0);
        //随机生成一个UUID对象
        String uuid = UUID.randomUUID().toString();
        announcement.setAnnouncementUuid(uuid);
        // 新增公告信息标志位
        Integer insert = null;
        insert = announcementMapper.insert(announcement);
        // 新增成功则通过WebSocket推送到前端显示
        if (insert != 0){
            sendAnnouncement();
            return Result.success("添加成功");
        }else {
            return Result.fail("添加失败");
        }
    }

    /**
     * 获取未读系统公告信息
     *
     * @return
     */
    public Result<List<announcement>> getAnnouncement(){
        QueryWrapper<announcement> wrapper = new QueryWrapper<>();
        wrapper.eq("announcement_is_read", 0).eq("announcement_is_delete", 0);
        List<announcement> announcements = announcementMapper.selectList(wrapper);
        return Result.success(announcements);
    }

    /**
     * 获取已读系统公告信息
     *
     * @return
     */
    public Result<List<announcement>> getReadAnnouncement(){
        QueryWrapper<announcement> wrapper = new QueryWrapper<>();
        wrapper.eq("announcement_is_read", 1).eq("announcement_is_delete", 0);
        List<announcement> announcements = announcementMapper.selectList(wrapper);
        return Result.success(announcements);
    }

    /**
     * 获取逻辑删除系统公告信息
     *
     * @return
     */
    public Result<List<announcement>> getDeleteAnnouncement(){
        QueryWrapper<announcement> wrapper = new QueryWrapper<>();
        wrapper.eq("announcement_is_read", 1).eq("announcement_is_delete", 1);
        List<announcement> announcements = announcementMapper.selectList(wrapper);
        return Result.success(announcements);
    }

    /**
     * 标记已读系统公告
     * @param uuid
     * @return
     */
    public Result<String> markRead(String uuid){
        // 如果uuid存在，则改变指定uuid通告为已读状态
        if (!"all".equals(uuid)){
            QueryWrapper<announcement> wrapper = new QueryWrapper<>();
            wrapper.eq("announcement_uuid", uuid);
            announcement announcement = announcementMapper.selectOne(wrapper);
            announcement.setAnnouncementIsRead(1);
            int update = announcementMapper.updateById(announcement);
            if (update != 0){
                return Result.success("已标记为已读");
            }else {
                return Result.fail("标记失败");
            }
        }else {
            // 如果uuid不存在，则改变所有通告状态为已读状态
            UpdateWrapper<announcement> wrapper = new UpdateWrapper<>();
            wrapper.eq("announcement_is_read",0);
            wrapper.set("announcement_is_read", 1);
            int update = announcementMapper.update(null, wrapper);
            if (update != 0){
                return Result.success("已全部标记为已读");
            }else {
                return Result.fail("标记失败");
            }
        }
    }

    /**
     * 逻辑删除系统公告
     * @param uuid
     * @return
     */
    public Result<String> markDelete(String uuid){
        // 如果uuid存在，则改变指定uuid通告为删除状态
        if (!"all".equals(uuid)){
            QueryWrapper<announcement> wrapper = new QueryWrapper<>();
            wrapper.eq("announcement_uuid", uuid);
            announcement announcement = announcementMapper.selectOne(wrapper);
            announcement.setAnnouncementIsDelete(1);
            int update = announcementMapper.updateById(announcement);
            if (update != 0){
                return Result.success("已删除");
            }else {
                return Result.fail("删除失败");
            }
        }else {
            // 如果uuid不存在，则改变所有通告状态为已读状态
            UpdateWrapper<announcement> wrapper = new UpdateWrapper<>();
            wrapper.eq("announcement_is_delete",0);
            wrapper.set("announcement_is_delete", 1);
            int update = announcementMapper.update(null, wrapper);
            if (update != 0){
                return Result.success("已全部删除");
            }else {
                return Result.fail("删除失败");
            }
        }
    }

    /**
     * 删除系统公告信息
     *
     * @return
     */
    public Result<String> DeleteAnnouncement(String uuid){
        // 通过uuid删除系统公告
        // 如果有uuid传入，则删除指定uuid的通告信息
        if (!"all".equals(uuid)){
            QueryWrapper<announcement> wrapper = new QueryWrapper<>();
            wrapper.eq("announcement_uuid", uuid);
            int delete = announcementMapper.delete(wrapper);
            if (delete != 0){
                return Result.success("删除成功");
            }else {
                return Result.fail("删除失败");
            }
        }else {
            // 如果不存在uuid，则删除所有逻辑删除状态下的所有通告
            QueryWrapper<announcement> wrapper = new QueryWrapper<>();
            wrapper.eq("announcement_is_delete", 1);
            List<announcement> announcements = announcementMapper.selectList(wrapper);
            if (announcements.size() != 0){
                int delete = announcementMapper.delete(wrapper);
                if (delete != 0){
                    return Result.success("删除成功");
                }
            }else {
                return Result.success("删除成功");
            }
        }
        return null;
    }

    /**
     * 还原系统通告
     *
     * @param uuid
     * @return
     */
    public Result<String> restore(String uuid){
        // 如果uuid存在，则还原指定uuid通告为已读状态
        if (!"all".equals(uuid)){
            QueryWrapper<announcement> wrapper = new QueryWrapper<>();
            wrapper.eq("announcement_uuid", uuid);
            announcement announcement = announcementMapper.selectOne(wrapper);
            announcement.setAnnouncementIsDelete(0);
            int update = announcementMapper.updateById(announcement);
            if (update != 0){
                return Result.success("还原成功");
            }else {
                return Result.fail("还原失败");
            }
        }else {
            // 如果uuid不存在，则改变所有通告状态为已读状态
            UpdateWrapper<announcement> wrapper = new UpdateWrapper<>();
            wrapper.eq("announcement_is_delete",1);
            wrapper.set("announcement_is_delete", 0);
            int update = announcementMapper.update(null, wrapper);
            if (update != 0){
                return Result.success("已全部还原成功");
            }else {
                return Result.fail("还原失败");
            }
        }
    }

    /**
     * WebSocket监听并5秒推送一次系统日志数据
     * @throws InterruptedException
     */
    @Scheduled(fixedRate = 10000)
    public void sendSystemLog() throws InterruptedException {
        Result<systemLog> systemLog = getSystemLog();
        socketOutMessage socketOutMessage = new socketOutMessage();
        socketOutMessage.setDataType("系统日志");
        ArrayList<systemLog> arrayList = new ArrayList<>();
        arrayList.add(systemLog.getData());
        socketOutMessage.setDataBody(arrayList);
        webSocketService.sendMessage(JSON.toJSONString(socketOutMessage));
    }

    /**
     * WebSocket监听并推送系统公告数据
     */
    public void sendAnnouncement(){
        Result<List<announcement>> announcement = getAnnouncement();
        socketOutMessage socketOutMessage = new socketOutMessage();
        socketOutMessage.setDataType("系统公告");
        ArrayList<List<announcement>> arrayList = new ArrayList<>();
        arrayList.add(announcement.getData());
        socketOutMessage.setDataBody(arrayList);
        webSocketService.sendMessage(JSON.toJSONString(socketOutMessage));
    }
}
